libobs_wrapper\data\output/
traits.rs1use std::{
2 collections::HashMap,
3 ffi::CStr,
4 fmt::Debug,
5 sync::{Arc, RwLock},
6};
7
8use crate::{
9 data::object::ObsObjectTrait,
10 encoders::{audio::ObsAudioEncoder, video::ObsVideoEncoder},
11 enums::ObsOutputStopSignal,
12 macros::trait_with_optional_send_sync,
13 run_with_obs,
14 runtime::ObsRuntime,
15 utils::{AudioEncoderInfo, ObsError, OutputInfo, VideoEncoderInfo},
16};
17
18use super::ObsOutputSignals;
19
20trait_with_optional_send_sync! {
21 pub(crate) trait ObsOutputTraitSealed: Debug {
22 fn new(output: OutputInfo, runtime: ObsRuntime) -> Result<Self, ObsError>
31 where
32 Self: Sized;
33 }
34}
35
36#[allow(private_bounds)]
37pub trait ObsOutputTrait: ObsOutputTraitSealed + ObsObjectTrait<*mut libobs::obs_output_t> {
38 fn signals(&self) -> &Arc<ObsOutputSignals>;
39
40 fn video_encoder(&self) -> &Arc<RwLock<Option<Arc<ObsVideoEncoder>>>>;
41 fn audio_encoders(&self) -> &Arc<RwLock<HashMap<usize, Arc<ObsAudioEncoder>>>>;
42
43 fn get_current_video_encoder(&self) -> Result<Option<Arc<ObsVideoEncoder>>, ObsError> {
45 let curr = self
46 .video_encoder()
47 .read()
48 .map_err(|e| ObsError::LockError(e.to_string()))?;
49
50 Ok(curr.clone())
51 }
52
53 fn create_and_set_video_encoder(
57 &mut self,
58 info: VideoEncoderInfo,
59 ) -> Result<Arc<ObsVideoEncoder>, ObsError> {
60 if self.is_active()? {
61 return Err(ObsError::OutputAlreadyActive);
62 }
63
64 let video_enc = ObsVideoEncoder::new_from_info(info, self.runtime().clone())?;
65
66 self.set_video_encoder(video_enc.clone())?;
67 Ok(video_enc)
68 }
69
70 fn set_video_encoder(&mut self, encoder: Arc<ObsVideoEncoder>) -> Result<(), ObsError> {
74 if self.is_active()? {
75 return Err(ObsError::OutputAlreadyActive);
76 }
77
78 let output_ptr = self.as_ptr();
79 let encoder_ptr = encoder.as_ptr();
80 let runtime = self.runtime().clone();
81
82 run_with_obs!(runtime, (output_ptr, encoder_ptr), move || {
83 unsafe {
84 libobs::obs_output_set_video_encoder(output_ptr.get_ptr(), encoder_ptr.get_ptr());
86 }
87 })?;
88
89 self.video_encoder()
90 .write()
91 .map_err(|e| ObsError::LockError(e.to_string()))?
92 .replace(encoder);
93
94 Ok(())
95 }
96
97 fn create_and_set_audio_encoder(
99 &mut self,
100 info: AudioEncoderInfo,
101 mixer_idx: usize,
102 ) -> Result<Arc<ObsAudioEncoder>, ObsError> {
103 if self.is_active()? {
104 return Err(ObsError::OutputAlreadyActive);
105 }
106
107 let audio_enc = ObsAudioEncoder::new_from_info(info, mixer_idx, self.runtime().clone())?;
108 self.set_audio_encoder(audio_enc.clone(), mixer_idx)?;
109 Ok(audio_enc)
110 }
111
112 fn set_audio_encoder(
116 &mut self,
117 encoder: Arc<ObsAudioEncoder>,
118 mixer_idx: usize,
119 ) -> Result<(), ObsError> {
120 if self.is_active()? {
121 return Err(ObsError::OutputAlreadyActive);
122 }
123
124 let encoder_ptr = encoder.as_ptr();
125 let output_ptr = self.as_ptr();
126 let runtime = self.runtime().clone();
127 run_with_obs!(runtime, (output_ptr, encoder_ptr), move || {
128 unsafe {
129 libobs::obs_output_set_audio_encoder(
131 output_ptr.get_ptr(),
132 encoder_ptr.get_ptr(),
133 mixer_idx,
134 );
135 }
136 })?;
137
138 self.audio_encoders()
139 .write()
140 .map_err(|e| ObsError::LockError(e.to_string()))?
141 .insert(mixer_idx, encoder);
142
143 Ok(())
144 }
145
146 fn start(&self) -> Result<(), ObsError> {
149 if self.is_active()? {
150 return Err(ObsError::OutputAlreadyActive);
151 }
152
153 let vid_encoder_ptr = self
154 .video_encoder()
155 .read()
156 .map_err(|e| ObsError::LockError(e.to_string()))?
157 .as_ref()
158 .map(|enc| enc.as_ptr());
159
160 let audio_encoder_pointers = self
161 .audio_encoders()
162 .read()
163 .map_err(|e| ObsError::LockError(e.to_string()))?
164 .values()
165 .map(|enc| enc.as_ptr())
166 .collect::<Vec<_>>();
167
168 let output_ptr = self.as_ptr();
169 let runtime = self.runtime().clone();
170 let res = run_with_obs!(
171 runtime,
172 (output_ptr, vid_encoder_ptr, audio_encoder_pointers),
173 move || {
174 if let Some(vid_encoder_ptr) = vid_encoder_ptr {
175 unsafe {
176 libobs::obs_encoder_set_video(
178 vid_encoder_ptr.get_ptr(),
179 libobs::obs_get_video(),
180 );
181 }
182 }
183 for audio_encoder_ptr in audio_encoder_pointers {
184 unsafe {
185 libobs::obs_encoder_set_audio(
187 audio_encoder_ptr.get_ptr(),
188 libobs::obs_get_audio(),
189 );
190 }
191 }
192
193 unsafe {
194 libobs::obs_output_start(output_ptr.get_ptr())
196 }
197 }
198 )?;
199
200 if res {
201 return Ok(());
202 }
203
204 let runtime = self.runtime().clone();
205 let err = run_with_obs!(runtime, (output_ptr), move || {
206 let err = unsafe {
207 libobs::obs_output_get_last_error(output_ptr.get_ptr())
209 };
210
211 if err.is_null() {
212 return "Unknown error".to_string();
213 }
214
215 let err = unsafe { CStr::from_ptr(err) };
216
217 let err = err.to_string_lossy().to_string();
218 err
219 })?;
220
221 Err(ObsError::OutputStartFailure(Some(err)))
222 }
223
224 fn set_paused(&self, should_pause: bool) -> Result<(), ObsError> {
225 if !self.is_active()? {
226 return Err(ObsError::OutputPauseFailure(Some(
227 "Output is not active.".to_string(),
228 )));
229 }
230
231 let output_ptr = self.as_ptr();
232 let runtime = self.runtime().clone();
233
234 let mut rx = if should_pause {
235 self.signals().on_pause()?
236 } else {
237 self.signals().on_unpause()?
238 };
239
240 let res = run_with_obs!(runtime, (output_ptr), move || {
241 unsafe {
242 libobs::obs_output_pause(output_ptr.get_ptr(), should_pause)
244 }
245 })?;
246
247 if res {
248 rx.blocking_recv().map_err(|_| ObsError::NoSenderError)?;
249
250 Ok(())
251 } else {
252 let runtime = self.runtime().clone();
253 let err = run_with_obs!(runtime, (output_ptr), move || {
254 let err = unsafe {
255 libobs::obs_output_get_last_error(output_ptr.get_ptr())
257 };
258
259 if err.is_null() {
260 return None;
261 }
262
263 let err = unsafe { CStr::from_ptr(err) };
264 let err = err.to_string_lossy().to_string();
265
266 Some(err)
267 })?;
268
269 Err(ObsError::OutputPauseFailure(err))
270 }
271 }
272
273 fn pause(&self) -> Result<(), ObsError> {
275 self.set_paused(true)
276 }
277
278 fn unpause(&self) -> Result<(), ObsError> {
279 self.set_paused(false)
280 }
281
282 fn stop(&mut self) -> Result<(), ObsError> {
284 let output_ptr = self.as_ptr();
285 let runtime = self.runtime().clone();
286 let output_active = run_with_obs!(runtime, (output_ptr), move || {
287 unsafe {
288 libobs::obs_output_active(output_ptr.get_ptr())
290 }
291 })?;
292
293 if !output_active {
294 return Err(ObsError::OutputStopFailure(Some(
295 "Output is not active.".to_string(),
296 )));
297 }
298
299 let mut rx = self.signals().on_stop()?;
300 let mut rx_deactivate = self.signals().on_deactivate()?;
301
302 let runtime = self.runtime().clone();
303 run_with_obs!(runtime, (output_ptr), move || {
304 unsafe {
305 libobs::obs_output_stop(output_ptr.get_ptr())
307 }
308 })?;
309
310 let signal = rx.blocking_recv().map_err(|_| ObsError::NoSenderError)?;
311
312 log::trace!("Received stop signal: {:?}", signal);
313 if signal != ObsOutputStopSignal::Success {
314 return Err(ObsError::OutputStopFailure(Some(signal.to_string())));
315 }
316
317 rx_deactivate
318 .blocking_recv()
319 .map_err(|_| ObsError::NoSenderError)?;
320
321 Ok(())
322 }
323
324 fn is_active(&self) -> Result<bool, ObsError> {
326 let output_ptr = self.as_ptr();
327 let runtime = self.runtime().clone();
328 let output_active = run_with_obs!(runtime, (output_ptr), move || {
329 unsafe {
330 libobs::obs_output_active(output_ptr.get_ptr())
332 }
333 })?;
334
335 Ok(output_active)
336 }
337}